package com.lin.service.Impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lin.po.User;
import com.lin.mapper.UserMapper;
import com.lin.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;
    private final Map<String, Method> methodCache = new ConcurrentHashMap<>();

    @Value("${queriedColumns.user.selectPartialFieldByNameAndBalance2:}")
    private String queriedColumns;

    @Override
    public void selectPartialFieldByNameAndBalance(Integer miniBalance) throws InvocationTargetException, IllegalAccessException {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        List<String> queriedFields = new ArrayList<>();

        // 通过反射获取User的所有属性名
        Field[] declaredFields = User.class.getDeclaredFields();
        List<String> fieldNames = Arrays.stream(declaredFields)
                .map(Field::getName)
                .toList();

        // 判断配置文件是否为空
        if (queriedColumns.isBlank()) {
            queriedFields.addAll(fieldNames);
        } else {
            // 获取配置文件中定义的要查询的属性名
            List<String> qcs = Arrays.stream(queriedColumns.split(","))
                    .map(String::trim)
                    .filter(s -> !s.isBlank())  // 过滤空字符串
                    .toList();

            // 判断配置文件中的属性名是否在User的所有属性名中
            for (String qc : qcs) {
                if (fieldNames.contains(qc)) queriedFields.add(qc);
            }
        }

        // 构造查询条件
        lambdaQueryWrapper.select(User.class,
                        s -> queriedFields.contains(s.getProperty()))
                .ge(User::getBalance, miniBalance);

        // 执行查询
        List<User> users = userMapper.selectList(lambdaQueryWrapper);

        // 预先获取Method方法对象
        Map<String, Method> methodMap = queriedFields.stream()
                .collect(Collectors.toMap(
                        filed -> filed,
                        filed -> {
                            try {
                                return User.class.getMethod("get" + StringUtils.capitalize(filed));
                            } catch (NoSuchMethodException e) {
                                throw new RuntimeException(e);
                            }
                        }
                ));

        // users中可能有些列为null, 因为只查询了部分字段, 只返回非null字段, 通过动态反射获取
        for (User user : users) {
            for (String queriedField : queriedFields) {
                // 遍历queriedFields, 获取字段名, 然后反射获取对应get方法
                System.out.print(methodMap.get(queriedField).invoke(user) + "\t");
                /* 这种方式效率较低, 每次循环都要反射获取方法对象
                String fieldName = "get" + StringUtils.capitalize(queriedField); // 拼接方法名
                Method method = user.getClass().getDeclaredMethod(fieldName); // 获取方法
                Object invoke = method.invoke(user); // 反射调用get方法
                System.out.print(invoke + "\t");*/
            }
            System.out.println();
        }
    }

    /**
     * 根据id批量减少余额
     * @param wrapper 查询条件
     * @param balance 操作金额
     */
    @Override
    public void minusBalanceByIds(LambdaQueryWrapper<User> wrapper, Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        userMapper.minusBalanceByIds(wrapper, balance);
    }
    @Override
    public void minusBalanceByIds(Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        userMapper.minusBalanceByIds(null, balance);
    }

    /**
     * 根据id批量增加余额
     * @param wrapper 查询条件
     * @param balance 操作金额
     */
    @Override
    public void addBalanceByIds(LambdaQueryWrapper<User> wrapper, Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        userMapper.addBalanceByIds(wrapper, balance);
    }
    @Override
    public void addBalanceByIds(Integer balance) {
        if (balance == null || balance <= 0) throw new IllegalArgumentException("Invalid balance value");
        userMapper.addBalanceByIds(null, balance);
    }

    /**
     * 根据条件查询用户信息
     * @param minBalance 最低余额
     * @return 符合条件的用户信息
     */
    private List<User> queryUsersWithCondition(int minBalance) {
        if (minBalance <= 0) throw new IllegalArgumentException("balance value must be greater than zero");
        LambdaQueryWrapper<User> wrapper = buildQueryWrapper(minBalance);
        initializeMethodCache();
        return userMapper.selectList(wrapper);
    }

    /**
     * 构建查询条件
     * @return LambdaQueryWrapper<User>
     */
    private LambdaQueryWrapper<User> buildQueryWrapper(Integer minBalance) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        List<String> fields = resolveQueryFields();

        wrapper.select(User.class, info -> fields.contains(info.getProperty()))
                .ge(User::getBalance, minBalance);
        return wrapper;
    }

    /**
     * 解析查询字段 -- 通过反射获取实体类的所有属性
     * @return List<String>
     */
    private List<String> resolveQueryFields() {
        if (queriedColumns.isBlank()) {
            return Arrays.stream(User.class.getDeclaredFields())
                    .map(Field::getName)
                    .collect(Collectors.toList());
        }

        return Arrays.stream(queriedColumns.split(","))
                .map(String::trim)
                .filter(s -> !s.isBlank())
                .filter(this::isValidField) // 这里要抛异常, 写在这里会让该方法太长, 提取为一个方法
                .collect(Collectors.toList());
    }

    /**
     * 获取实体类中的属性
     * @param fieldName 属性名
     * @return 如果属性存在, 返回true, 否则返回false
     */
    private boolean isValidField(String fieldName) {
        try {
            User.class.getDeclaredField(fieldName);
            return true;
        } catch (NoSuchFieldException e) {
            return false;
        }
    }

    /**
     * 通过反射初始化Method方法对象
     */
    private void initializeMethodCache() {
        Arrays.stream(User.class.getDeclaredFields())
                .forEach(field -> {
                    String fieldName = field.getName();
                    try {
                        Method method = User.class.getMethod("get" + StringUtils.capitalize(fieldName));
                        methodCache.put(fieldName, method);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException("字段 " + fieldName + " 缺少对应的get方法", e);
                    }
                });
    }

    /**
     * 打印用户信息
     * @param users 符合条件的用户列表
     * @param fields 实体类的所有属性
     */
    private void printUsers(List<User> users, List<String> fields) {
        users.forEach(user -> {
            fields.forEach(field -> {
                try {
                    System.out.print(methodCache.get(field).invoke(user) + "\t");
                } catch (Exception e) {
                    System.out.print("N/A\t");
                }
            });
            System.out.println();
        });
    }

    /**
     * 查询用户信息
     * @param miniBalance 最低余额
     */
    public void queryUsers(Integer miniBalance) {
        List<User> users = this.queryUsersWithCondition(miniBalance);
        List<String> fields = this.resolveQueryFields();
        this.printUsers(users, fields);
    }


}
